#pragma once

#include <rocksdb/db.h>
#include <rocksdb/env.h>

#include <mutex>

#include "zrpc/base/logger.h"
#include "zrpc/net/event_loop.h"
#include "raft/context.h"
#include "raft/snapshot.h"

namespace libraft {
class Meta;
class Context;
class Raft;

typedef std::function<rocksdb::Status()> ApplyLogCallback;

class Apply {
 public:
  Apply(zrpc::EventLoop* loop, const std::shared_ptr<Context>& context,
        const std::shared_ptr<rocksdb::DB>& db,
        const std::shared_ptr<Meta>& meta,
        const std::shared_ptr<RaftLog>& raft_log, Raft* raft);

  ~Apply();

  void SetApplyLogCallback(ApplyLogCallback&& callback);
  void ScheduleApply();

  rocksdb::Status DefaultApplyLogCallback();

 private:
  void ApplyStateMachine();

  rocksdb::Status ApplyLog(const raft::Entry& entry);
  rocksdb::Status MembershipChange(const std::string& ip_port, bool add);

 private:
  Apply(const Apply&);
  void operator=(const Apply&);

  zrpc::EventLoop* loop_;
  Raft* const raft_;
  const std::shared_ptr<rocksdb::DB> apply_db_;
  const std::shared_ptr<Context> context_;
  const std::shared_ptr<RaftLog> raft_log_;
  const std::shared_ptr<Meta> meta_db_;
  std::unique_ptr<Snapshot> snapshot_;
  ApplyLogCallback apply_log_callback_;
};

}  // namespace libraft
